﻿using org.xisoft.game.utils.game.algorithms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace org.xisoft.game.utils.game
{
    [Serializable]
    public abstract class ChessBoardBase : ICloneable
    {
        public virtual object Clone()
        {

            ChessBoardBase copy = FormatterServices.GetUninitializedObject(GetType()) as ChessBoardBase;

            MemoryStream ms = new MemoryStream();
            BinaryReader br = new BinaryReader(ms);
            Save(new BinaryWriter(ms));
            
            ms.Seek(0, SeekOrigin.Begin);
            copy.Read(br);

            return copy;
        }

        public virtual ChessItem ReadItem(BinaryReader br)
        {
            String typeName = br.ReadString();
            Type type = Type.GetType(typeName);
            ChessItem item = Activator.CreateInstance(type) as ChessItem;

            item.PositionX = br.ReadByte();
            item.PositionY = br.ReadByte();
            item.Index = br.ReadByte();
            item.IsBlack = br.ReadBoolean();

            return item;
        }

        public virtual void DoNextMove(MoveAction moveAction)
        {
            moveAction.OldChessman = this[moveAction.ToX, moveAction.ToY];
            if (moveAction.CurrentChessman.PositionX >= 0 && moveAction.CurrentChessman.PositionY >= 0)
                this[moveAction.CurrentChessman.PositionX, moveAction.CurrentChessman.PositionY] = null;

            moveAction.FromX = moveAction.CurrentChessman.PositionX;
            moveAction.FromY = moveAction.CurrentChessman.PositionY;

            broad[moveAction.ToX, moveAction.ToY] = moveAction.CurrentChessman;
            moveAction.CurrentChessman.PositionX = moveAction.ToX;
            moveAction.CurrentChessman.PositionY = moveAction.ToY;
            IsCurrentBlackMove = !IsCurrentBlackMove;
            history.Push(moveAction);
        }

        public virtual void Read(BinaryReader br)
        {
            MaxX = br.ReadByte();
            MaxY = br.ReadByte();
            broad = new ChessItem[MaxX, MaxY];
            history = new Stack<MoveAction>();

            IsCurrentBlackMove = br.ReadBoolean();
            while (br.BaseStream.Position < br.BaseStream.Length)
            {
                ChessItem item= ReadItem(br);
                broad[item.PositionX, item.PositionY] = item;
            }
        }

        protected virtual void SaveItem(ChessItem item , BinaryWriter bw)
        {
            bw.Write(item.GetType().AssemblyQualifiedName);
            bw.Write((byte)item.PositionX);
            bw.Write((byte)item.PositionY);
            bw.Write(item.Index);
            bw.Write(item.IsBlack);
        }
        public virtual void Save(BinaryWriter bw)
        {
            bw.Write(MaxX);
            bw.Write(MaxY);
            bw.Write(IsCurrentBlackMove);
            for (byte i = 0; i < MaxX; i++)
                for (byte j = 0; j < MaxY; j++)
                {
                    ChessItem item = broad[i, j];
                    if (item != null)
                        SaveItem(item, bw);
                }
        }


        public Boolean IsCurrentBlackMove
        {
            get;
            private set;
        }

        public virtual void Undo()
        {
            if (history.Count <= 0) return;
            MoveAction moveAction = history.Pop();
            Undo(moveAction);
            IsCurrentBlackMove = !IsCurrentBlackMove;
        }
        protected abstract void Undo(MoveAction moveAction);


        public virtual void Reset()
        {
            broad = new ChessItem[MaxX, MaxY];
            history = new Stack<MoveAction>();
        }


        private ChessItem[,] broad ;
        public byte MaxX { get; private set; }
        public byte MaxY { get; private set; }

        public ChessBoardBase(byte maxX, byte maxY)
        {
            MaxX = maxX;
            MaxY = maxY;
            broad = new ChessItem[maxX, maxY];
            Reset();
        }

        public ChessItem this[int x, int y]
        {
            get { return broad[x, y]; }
            set { broad[x, y] = value; }
        }

        private Stack<MoveAction> history;
    }
}
